/* Tencent is pleased to support the open source community by making Hippy available.
 * Copyright (C) 2018 THL A29 Limited, a Tencent company. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.tencent.mtt.hippy.views.list;

import android.view.View;
import android.view.ViewGroup;
import com.tencent.mtt.hippy.uimanager.HippyViewEvent;
import com.tencent.renderer.node.ListItemRenderNode;
import com.tencent.renderer.node.PullFooterRenderNode;
import com.tencent.renderer.node.PullHeaderRenderNode;
import com.tencent.mtt.hippy.uimanager.RenderManager;
import com.tencent.renderer.node.RenderNode;
import com.tencent.mtt.hippy.utils.LogUtils;
import com.tencent.mtt.hippy.views.refresh.HippyPullFooterView;
import com.tencent.mtt.hippy.views.refresh.HippyPullHeaderView;
import com.tencent.mtt.supportui.views.recyclerview.*;
import com.tencent.renderer.NativeRender;

import com.tencent.renderer.utils.DiffUtils;
import java.util.ArrayList;
import java.util.Map;

@SuppressWarnings("deprecation")
public class HippyListAdapter extends RecyclerAdapter implements IRecycleItemTypeChange {

    protected final NativeRender nativeRenderer;
    private RecyclerViewBase.Recycler mRecycler;
    private HippyViewEvent onEndReachedEvent;
    private HippyViewEvent onLoadMoreEvent;
    // --Commented out by Inspection (2021/5/4 20:54):private static final String			TAG	= "HippyListAdapter";
    // harryguo: 给hippy sdk提供API：设置提前预加载的条目数量，默认为0
    private int mPreloadItemNum = 0;

    public HippyListAdapter(RecyclerView recyclerView, NativeRender nativeRenderer) {
        super(recyclerView);
        this.nativeRenderer = nativeRenderer;
    }

    @Override
    public String getViewHolderReUseKey(int position) {
        if (position < 0 || position > getItemCount()) {
            return null;
        }

        return String.valueOf(position);
    }

    @Override
    public RecyclerView.ViewHolderWrapper onCreateSuspendViewHolderWithPos(RecyclerViewBase parent,
            int position, int viewType) {
        return null;
    }

    @Override
    public ContentHolder onCreateContentViewWithPos(ViewGroup parent, int position, int viewType) {
        NodeHolder contentHolder = new NodeHolder();
        //LogUtils.d("HippyListView", "onCreateContentViewWithPos start position " + position);
        RenderNode contentViewRenderNode = RenderManager.getRenderNode(mParentRecyclerView).getChildAt(position);
        contentViewRenderNode.setLazy(false);
        View view = contentViewRenderNode.prepareHostViewRecursive();
        contentHolder.mContentView = view;
        if (view instanceof HippyPullHeaderView) {
            ((HippyPullHeaderView) view).setRecyclerView(mParentRecyclerView);
        }
        if (view instanceof HippyPullFooterView) {
            ((HippyPullFooterView) view).setRecyclerView(mParentRecyclerView);
        }
        contentHolder.mBindNode = contentViewRenderNode;
        contentHolder.isCreated = true;
        //LogUtils.d("HippyListView", "onCreateContentViewWithPos end position " + position);
        //LogUtils.d("HippyListView", "onCreateContentViewWithPos" + contentViewRenderNode);
        return contentHolder;
    }


    @Override
    protected void onViewAbandon(RecyclerView.ViewHolderWrapper viewHolder) {
        // set is lazy true the holder is delete so delete view
        NodeHolder nodeHolder = (NodeHolder) viewHolder.mContentHolder;

        if (nodeHolder.mBindNode != null && !nodeHolder.mBindNode.isDeleted()) {
            //LogUtils.d("HippyListView", "onViewAbandon start " + nodeHolder.mBindNode.toString());
            nodeHolder.mBindNode.setLazy(true);
            RenderNode parentNode = nodeHolder.mBindNode.getParent();
            if (parentNode != null) {
                nativeRenderer.getRenderManager().getControllerManager()
                        .deleteChild(parentNode.getRootId(), parentNode.getId(), nodeHolder.mBindNode.getId(), true);
            }
            //LogUtils.d("HippyListView", "onViewAbandon end " + nodeHolder.mBindNode.toString());
        }
        if (nodeHolder.mBindNode instanceof ListItemRenderNode) {
            //LogUtils.d("HippyListView", "onViewAbandon start " + nodeHolder.mBindNode.toString());
            ((ListItemRenderNode) nodeHolder.mBindNode).setRecycleItemTypeChangeListener(null);
        }
        super.onViewAbandon(viewHolder);
    }

    @Override
    public void onBindContentView(ContentHolder holder, int position, int layoutType) {
        NodeHolder contentHolder = (NodeHolder) holder;
        //LogUtils.d("HippyListView", "onBindContentView : " + position);
        if (contentHolder.isCreated) {
            contentHolder.mBindNode.mountHostViewRecursive();
//			nativeRenderer.getGlobalConfigs().getLogAdapter().log(TAG," onBindContentView updateViewRecursive");
            contentHolder.isCreated = false;
        } else {
            //step 1: diff
            contentHolder.mBindNode.setLazy(true);
            RenderNode toNode = null;
            try {
                toNode = RenderManager.getRenderNode(mParentRecyclerView).getChildAt(position);
            } catch (Throwable e) {
                LogUtils.d("HippyListAdapter", "onBindContentView: " + e.getMessage());
            }
            //保护下
            if (toNode == null || !(toNode instanceof ListItemRenderNode)) {
                return;
            }
            ((ListItemRenderNode) toNode).onBindViewHolder(contentHolder.mBindNode, contentHolder.mContentView);
            contentHolder.mBindNode = toNode;
        }
        if (contentHolder.mBindNode instanceof ListItemRenderNode) {
            ((ListItemRenderNode) contentHolder.mBindNode).setRecycleItemTypeChangeListener(this);
        }
    }


    @Override
    public boolean hasCustomRecycler() {
        return true;
    }

    RecyclerViewBase.ViewHolder findBestHolderRecursive(int position, int targetType,
            RecyclerViewBase.Recycler recycler) {
        RecyclerViewBase.ViewHolder matchHolder = getScrapViewForPositionInner(position, targetType,
                recycler);
        if (matchHolder == null) {
            matchHolder = recycler.getViewHolderForPosition(position);
        }

        if (matchHolder != null && ((NodeHolder) matchHolder.mContentHolder).mBindNode.isDeleted()) {
            matchHolder = findBestHolderRecursive(position, targetType, recycler);
        }

        return matchHolder;
    }

    ArrayList<RecyclerViewBase.ViewHolder> mListViewHolder;

    public int getRecyclerItemCount() {
        mListViewHolder = new ArrayList<>();

        RecyclerViewBase.Recycler recycler = mParentRecyclerView.getRecycler();

        mListViewHolder.addAll(recycler.mAttachedScrap);

        mListViewHolder.addAll(recycler.mCachedViews);

        for (int i = 0; i < recycler.getRecycledViewPool().mScrap.size(); i++) {
            mListViewHolder.addAll(recycler.getRecycledViewPool().mScrap.valueAt(i));
        }
        return mListViewHolder.size() + mParentRecyclerView.getChildCount();
    }

    View getRecyclerItemView(int index) {
        if (index < mListViewHolder.size()) {
            return mListViewHolder.get(index).mContent;
        } else {
            return mParentRecyclerView.getChildAt(index - mListViewHolder.size());
        }

    }

    @Override
    public RecyclerViewBase.ViewHolder findBestHolderForPosition(int position,
            RecyclerViewBase.Recycler recycler) {
        LogUtils.d("HippyListView", "findBestHolderForPosition start : " + position);
        mRecycler = recycler;
        int targetType = getItemViewType(position);
        RecyclerViewBase.ViewHolder matchHolder = findBestHolderRecursive(position, targetType,
                recycler);
        LogUtils.d("HippyListView", "findBestHolderForPosition end : " + position);
        return matchHolder;
    }

    @Override
    public RecyclerViewBase.ViewHolder findSuspendHolderForPosition(int position,
            RecyclerViewBase.Recycler recycler) {
        mRecycler = recycler;
        int targetType = getItemViewType(position);
        RecyclerViewBase.ViewHolder matchHolder = getScrapViewForPositionInner(position, targetType,
                recycler);
        if (matchHolder != null && ((NodeHolder) matchHolder.mContentHolder).mBindNode.isDeleted()) {
            return null;
        }
        return matchHolder;
    }

    private RecyclerViewBase.ViewHolder getScrapViewForPositionInner(int position, int type,
            RecyclerViewBase.Recycler recycler) {
        if (RenderManager.getRenderNode(mParentRecyclerView) == null
                ||
                RenderManager.getRenderNode(mParentRecyclerView)
                        .getChildCount()
                        <= position) {
            return null;
        }
        final int scrapCount = recycler.mAttachedScrap.size();
        // Try first for an exact, non-invalid match from scrap.
        for (int i = 0; i < scrapCount; i++) {
            final RecyclerViewBase.ViewHolder holder = recycler.mAttachedScrap.get(i);
            if (holder.getPosition() == position && !holder.isInvalid() && (!holder.isRemoved())) {
                if (holder.getItemViewType() == type
                        && holder.mContentHolder instanceof NodeHolder) {
                    RenderNode holderNode = ((NodeHolder) holder.mContentHolder).mBindNode;
                    RenderNode toNode = RenderManager.getRenderNode(mParentRecyclerView).getChildAt(position);
                    if (holderNode == toNode) {
                        recycler.mAttachedScrap.remove(i);
                        holder.setScrapContainer(null);
                        return holder;
                    }
                }
            }
        }
        // Search in our first-level recycled view cache.
        final int cacheSize = recycler.mCachedViews.size();
        for (int i = 0; i < cacheSize; i++) {
            final RecyclerViewBase.ViewHolder holder = recycler.mCachedViews.get(i);
            if (holder.getPosition() == position && holder.getItemViewType() == type && !holder
                    .isInvalid()
                    && holder.mContentHolder instanceof NodeHolder) {
                RenderNode holderNode = ((NodeHolder) holder.mContentHolder).mBindNode;
                RenderNode toNode = RenderManager.getRenderNode(mParentRecyclerView).getChildAt(position);
                if (holderNode == toNode) {
                    recycler.mCachedViews.remove(i);
                    return holder;
                }
            }
        }
        // Give up. Head to the shared pool.
        return this.getRecycledViewFromPoolInner(recycler.getRecycledViewPool(), type, position);
    }

    private RecyclerViewBase.ViewHolder getRecycledViewFromPoolInner(
            RecyclerViewBase.RecycledViewPool pool, int viewType, int position) {
        if (pool != null) {
            final ArrayList<RecyclerViewBase.ViewHolder> scrapHeap = pool.mScrap.get(viewType);
            if (scrapHeap != null && !scrapHeap.isEmpty()) {
                // traverse all scrap
                for (RecyclerViewBase.ViewHolder holder : scrapHeap) {
                    if (holder.getItemViewType() == viewType
                            && holder.mContentHolder instanceof NodeHolder) {
                        RenderNode holderNode = ((NodeHolder) holder.mContentHolder).mBindNode;
                        RenderNode toNode = RenderManager.getRenderNode(mParentRecyclerView).getChildAt(position);
                        if (holderNode == toNode) {
                            scrapHeap.remove(holder);
                            return holder;
                        }
                    }
                }
            }
        }
        return null;
    }

    private void checkHolderType(int oldType, int newType, ListItemRenderNode listItemRenderNode) {
        //do checkHolderType onScreen
        int count = mParentRecyclerView.getChildCount();
        for (int i = 0; i < count; i++) {
            final RecyclerViewBase.ViewHolder holder = mParentRecyclerView
                    .getChildViewHolder(mParentRecyclerView.getChildAt(i));
            if (holder.getItemViewType() == oldType
                    && holder.mContentHolder instanceof NodeHolder) {
                RenderNode holderNode = ((NodeHolder) holder.mContentHolder).mBindNode;
                if (holderNode == listItemRenderNode) {
                    holder.setItemViewType(newType);
                    return;
                }
            }
        }

        //do checkHolderType inCache
        final int scrapCount = mRecycler.mAttachedScrap.size();
        // Try first for an exact, non-invalid match from scrap.
        for (int i = 0; i < scrapCount; i++) {
            final RecyclerViewBase.ViewHolder holder = mRecycler.mAttachedScrap.get(i);

            if (holder.getItemViewType() == oldType
                    && holder.mContentHolder instanceof NodeHolder) {
                RenderNode holderNode = ((NodeHolder) holder.mContentHolder).mBindNode;
                if (holderNode == listItemRenderNode) {
                    holder.setItemViewType(newType);
                    return;
                }
            }
        }

        // Search in our first-level recycled view cache.
        final int cacheSize = mRecycler.mCachedViews.size();
        for (int i = 0; i < cacheSize; i++) {
            final RecyclerViewBase.ViewHolder holder = mRecycler.mCachedViews.get(i);
            if (holder.getItemViewType() == oldType
                    && holder.mContentHolder instanceof NodeHolder) {
                RenderNode holderNode = ((NodeHolder) holder.mContentHolder).mBindNode;
                if (holderNode == listItemRenderNode) {
                    holder.setItemViewType(newType);
                    return;
                }
            }
        }

        // Give up. Head to the shared pool.
        if (mRecycler.getRecycledViewPool() != null) {
            final ArrayList<RecyclerViewBase.ViewHolder> scrapHeap = mRecycler
                    .getRecycledViewPool().mScrap.get(oldType);
            if (scrapHeap != null && !scrapHeap.isEmpty()) {
                // traverse all scrap
                for (RecyclerViewBase.ViewHolder holder : scrapHeap) {
                    if (holder.getItemViewType() == oldType
                            && holder.mContentHolder instanceof NodeHolder) {
                        RenderNode holderNode = ((NodeHolder) holder.mContentHolder).mBindNode;
                        if (holderNode == listItemRenderNode) {
                            holder.setItemViewType(newType);
                            scrapHeap.remove(holder);
                            mRecycler.getRecycledViewPool().getScrapHeapForType(newType)
                                    .add(holder);
                            return;
                        }
                    }
                }
            }
        }
    }

    @Override
    public int getCustomHeaderViewWidth() {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null && listNode.getChildCount() > 0) {
            RenderNode listItemNode = listNode.getChildAt(0);
            if (listItemNode instanceof PullHeaderRenderNode) {
                return listItemNode.getWidth();
            }
        }

        return 0;
    }

    @Override
    public int getCustomFooterViewWidth() {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null && listNode.getChildCount() > 0) {
            RenderNode listItemNode = listNode.getChildAt(listNode.getChildCount() - 1);
            if (listItemNode instanceof PullFooterRenderNode) {
                return listItemNode.getWidth();
            }
        }

        return 0;
    }

    @Override
    public int getCustomHeaderViewHeight() {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null && listNode.getChildCount() > 0) {
            RenderNode listItemNode = listNode.getChildAt(0);
            if (listItemNode instanceof PullHeaderRenderNode) {
                return listItemNode.getHeight();
            }
        }

        return 0;
    }

    @Override
    public int getCustomFooterViewHeight() {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null && listNode.getChildCount() > 0) {
            RenderNode listItemNode = listNode.getChildAt(listNode.getChildCount() - 1);
            if (listItemNode instanceof PullFooterRenderNode) {
                return listItemNode.getHeight();
            }
        }

        return 0;
    }

    @Override
    public int getItemHeight(int index) {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null && listNode.getChildCount() > index && index >= 0) {
            RenderNode listItemNode = listNode.getChildAt(index);
            if (listItemNode != null) {
                return listItemNode.getHeight();
            }
        }
        return 0;
    }

    @Override
    public int getItemWidth(int index) {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null && listNode.getChildCount() > index && index >= 0) {
            RenderNode listItemNode = listNode.getChildAt(index);
            if (listItemNode != null) {
                return listItemNode.getWidth();
            }
        }
        return 0;
    }

    @Override
    public int getTotalHeight() {
        if (isAutoCalculateItemHeight()) {
            mContentHeight = -1;
        }
        if (mContentHeight == -1) {
            int itemCount = getItemCount();
            mContentHeight = 0;

            if (mParentRecyclerView.mLayoutType == RecyclerViewBase.LAYOUT_TYPE_LIST) {
                for (int i = 0; i < itemCount; i++) {
                    if (mParentRecyclerView.mLayout.canScrollHorizontally()) {
                        mContentHeight += getItemWidth(i);
                        mContentHeight += getItemMaigin(LOCATION_LEFT, i);
                        mContentHeight += getItemMaigin(LOCATION_RIGHT, i);
                    } else {
                        mContentHeight += getItemHeight(i);
                        mContentHeight += getItemMaigin(LOCATION_TOP, i);
                        mContentHeight += getItemMaigin(LOCATION_BOTTOM, i);
                    }
                }
            }
        }

        int footerViewSize = mParentRecyclerView.mLayout.canScrollHorizontally() ?
                getCustomFooterViewWidth() : getCustomFooterViewHeight();
        return mContentHeight - footerViewSize;
    }

    @Override
    public int getItemCount() {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null) {
            return listNode.getChildCount();
        }
        return super.getItemCount();
    }

    @Override
    public int getItemViewType(int index) {
        RenderNode listViewNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listViewNode != null && listViewNode.getChildCount() > index) {
            RenderNode listItemNode = RenderManager.getRenderNode(mParentRecyclerView).getChildAt(index);
            if (listItemNode != null) {
                if (listItemNode instanceof PullFooterRenderNode) {
                    return RecyclerViewBase.ViewHolder.TYPE_CUSTOM_FOOTER;
                }

                if (listItemNode instanceof PullHeaderRenderNode) {
                    return RecyclerViewBase.ViewHolder.TYPE_CUSTOM_HEADERE;
                }

                if (listItemNode.getProps() != null) {
                    Map<String, Object> listItemProps = listItemNode.getProps();
                    if (listItemProps.get(ListItemRenderNode.ITEM_VIEW_TYPE) instanceof Number) {
                        return ((Number) listItemProps.get(ListItemRenderNode.ITEM_VIEW_TYPE)).intValue();
                    }
                }
            }
        }
        return super.getItemViewType(index);
    }

    @Override
    public boolean isSuspentedItem(int pos) {
        RenderNode listNode = RenderManager.getRenderNode(mParentRecyclerView);
        if (listNode != null && listNode.getChildCount() > pos) {
            RenderNode listItemNode = listNode.getChildAt(pos);
            if (listItemNode instanceof ListItemRenderNode) {
                return ((ListItemRenderNode) listItemNode).shouldSticky();
            }
        }
        return super.isSuspentedItem(pos);
    }

    @Override
    public boolean isAutoCalculateItemHeight() {
        return true;
    }

    @Override
    public void onRecycleItemTypeChanged(int oldType, int newType,
            ListItemRenderNode listItemNode) {
        checkHolderType(oldType, newType, listItemNode);
    }

    @Override
    public void notifyEndReached() {
        getOnEndReachedEvent().send(mParentRecyclerView, null);
        getOnLoadMoreEvent().send(mParentRecyclerView, null);
    }

    @Override
    public int getPreloadThresholdInItemNumber() {
        return mPreloadItemNum;
    }

    @Override
    public void onPreload() {
        getOnEndReachedEvent().send(mParentRecyclerView, null);
        getOnLoadMoreEvent().send(mParentRecyclerView, null);
    }

    protected void setPreloadItemNumber(int preloadItemNum) {
        mPreloadItemNum = preloadItemNum;
    }

    protected HippyViewEvent getOnEndReachedEvent() {
        if (onEndReachedEvent == null) {
            onEndReachedEvent = new HippyViewEvent("endreached");
        }
        return onEndReachedEvent;
    }

    protected HippyViewEvent getOnLoadMoreEvent() {
        if (onLoadMoreEvent == null) {
            onLoadMoreEvent = new HippyViewEvent("loadmore");
        }
        return onLoadMoreEvent;
    }
}
